/*
Kiwi Editor is a framework for building WYSIWYG web editors.
Copyright (C) 2011  Roosevelt de Lima Sardinha

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

Contact the author at roosevelt1@gmail.com (Roosevelt) 
*/

/**
 * The box where the properties of the elements that were put in the edition area are shown. 
 * It has tabs depending on the number of selected elements at one time. 
 * @author Roosevelt de Lima Sardinha
 * @constructor
 */
function propertyBox(id, properties){
    
	/**
	 * The workspace this property box belongs to.
	 * @type {Object}  
	 */
	this.workspace = properties.workspace;
    var thisObject = this;
	
	/**
	 * The unique id of the property box.
	 * @type {String}
	 */
	this.id = id;
	
	/**
	 * The object that contains the initial properties of the property box.
	 * @type {Object} 
	 */
	this.properties = properties;
	
	/**
	 * The path of the CSS stylesheet file.
	 * @type {String}
	 */
	this.cssPath = this.properties.cssPath ? this.properties.cssPath : "css/boxes/propertyBox.css";
	
	/**
	 * The title of this property box.
 	 * @type {String}
	 */
	this.title = this.properties.title ? this.properties.title: "PropertyBox";
	
	/**
	 * The left coordinate of the property box.
	 * @type {Int}
	 */	
	this.left = this.properties.left ? parseInt(this.properties.left) : 0;
	
	/**
	 * The top coordinate of the property box.
	 * @type {Int}
	 */ 	
	this.top = this.properties.top ? parseInt(this.properties.top) : 0;
	
	/**
	 * The width of the property box.
	 * @type {Int}
	 */
	this.width = this.properties.width ? parseInt(this.properties.width) : 100;
	
	/**
	 * The height of the property box.
	 * @type {Int}
	 */	
	this.height = this.properties.height ? parseInt(this.properties.height) : 100;
	
	/** 
	 * The id of the DOM node that contains this property box.
	 * @type {String}
	 */
    this.parentId = this.properties.parent;
	
	/**
	 * Determines if the property box has the focus of the keyboard.
	 * @type {Boolean}
	 */
	this.hasFocus = false;
	
	// Events
	/**
	 * <b>Event</b><br/>
	 * Defines what is going to be done when you drag the property box.
	 * @param {Int} x The left coordinate of the property box while dragging.
	 * @param {Int} y The top coordinate of the property box while dragging.
	 * @param {Object} propertyBox The property box being dragged.
	 */	
	this.onDrag = function(x, y, propertyBox){};
	
	/**
	 * <b>Event</b><br/>
	 * Defines what is going to be done when you drop the property box.
	 * @param {Int} x The left coordinate of the property box when dropped.
	 * @param {Int} y The top coordinate of the property box when dropped.
	 * @param {Object} propertyBox The property box being dropped.
	 */	
	this.onDrop = function(x,y, propertyBox){};
	
	/**
	 * <b>Event</b><br/>
	 * Defines what is going to be done when you start to resize the property box.
	 * @param {Int} width The width of the property box when you start to resize.
	 * @param {Int} height The height of the property box when you start to resize.
	 * @param {Object} propertyBox The property box being resized.
	 */	
	this.onBeforeResize = function(width, height, propertyBox){};
	
	/**
	 * <b>Event</b><br/>
	 * Defines what is going to be done during resize.
	 * @param {Int} width The width of the property box during resize.
	 * @param {Int} height The height of the property box during resize.
	 * @param {Object} propertyBox The property box being resized.
	 */
	this.onResize = function(width, height, propertyBox){};
	
	/**
	 * <b>Event</b><br/>
	 * Defines what is going to be done after resize.
	 * @param {Int} width The width of the property box after resize.
	 * @param {Int} height The height of the property box after resize.
	 * @param {Object} propertyBox The property box resized.
	 */	
	this.onAfterResize = function(width, height, propertyBox){};
	
	/**
	 * <b>Event</b><br/>
	 * Defines what is going to be done when you remove this property box.
	 */
	this.onRemove = function(){};
	
	/**
	 * <b>Event</b><br/>
	 * Defines what is going to be done when you restore the size of this property box.
	 * @param {Object} propertyBox The property box the size was restored.
	 */	
	this.onRestoreSize = function(propertyBox){};
	
	/**
	 * <b>Event</b><br/>
	 * Called when you add a new tab to the property box.
	 * @param {String} title The title of the tab. 
	 * @param {Int} position The position where the tab is being inserted.
	 * @param {Object} propertyBox The property box where the tab was inserted.
	 */
	this.onAddTab = function(title, position, propertyBox){};
	
	/**
	 * <b>Event</b><br/>
	 * Called when a tab is removed.
	 * @param {Int} position The position of the tab. 
	 * @param {Object} propertyBox The property box from where the tab was removed.
	 */
	this.onRemoveTab = function(position, propertyBox){};
	
	/**
	 * <b>Event</b><br/>
	 * Defines what is going to be done when you add a property.
	 * @param {Int} tabId The id of the tab received when it was added. 
	 * @param {Int} row The row in the table inside the tab where the content is going to be inserted.
	 * @param {Int} cell The cell in the table inside the tab where the content is going to be inserted.
	 * @param {String} property The property of the EO to be changed by this field.
	 * @param {String} inputType The type of input to be inserted. Eg.: Text, Color, FontColor
	 * @param {Object} EO The EO that is going to have its property changed.
	 * @param {Object} properties The properties you may use depending on the type of field of your choice. 
	 * @param {Object} thisObject The property box the property is being added.
	 */
	this.onAddProperty = function(tabId, row, cell, property, inputType, EO, properties, thisObject){};
	
	/**
	 * <b>Event</b><br/>
	 * Called when removing all properties at once.
	 * @param {Int} tabId The id of the tab given when it was created.
	 * @param {Object} propertyBox The property box form where the properties where removed.
	 */
	this.onRemoveAllProperties = function(tabId, propertyBox){};

    var parent = document.getElementById(this.parentId);
    
    //gets the initial size and position of the object
    var iniLeft = parseInt(properties.left);
    var iniTop = parseInt(properties.top);
    var iniWidth = parseInt(properties.width);
    var iniHeight = parseInt(properties.height);
    
    //code for the divs: background of the object and the div where the user works	
    
    var propertyBoxBg = document.createElement('div');
    propertyBoxBg.id = this.id + 'Bg';
    propertyBoxBg.className = 'property-box';
    propertyBoxBg.style.borderWidth = '4px';
    propertyBoxBg.style.position = 'absolute';
    propertyBoxBg.style.left = this.left + 'px';
    propertyBoxBg.style.top = this.top + 'px';
    propertyBoxBg.style.width = this.width + 'px';
    propertyBoxBg.style.height = this.height + 'px';
    
    //generates the property box
    var propertyBox = document.createElement('div');
	/**
	 * @ignore
	 */
    propertyBox.id = this.id;
    propertyBox.style.position = 'absolute';
    propertyBox.style.left = this.left + parseInt(propertyBoxBg.style.borderLeftWidth) + 'px';
    propertyBox.style.top = this.top + parseInt(propertyBoxBg.style.borderTopWidth) + 'px';
    propertyBox.style.width = this.width + 'px';
    propertyBox.style.height = this.height + 'px';
    
    // A copy of the background div is generate to solve problems like opacity inheritance and borders size
    var propertyBoxHeader = document.createElement('div');
    propertyBoxHeader.id = this.id + 'Header';
    propertyBoxHeader.className = 'property-box-header';
    propertyBoxHeader.innerHTML = this.title;
    propertyBoxHeader.ondblclick = function(){
        thisObject.setLeft(iniLeft);
        thisObject.setTop(iniTop);
        thisObject.setWidth(iniWidth);
        thisObject.setHeight(iniHeight);
		thisObject.onRestoreSize(thisObject);
    };
    propertyBox.appendChild(propertyBoxHeader);
    
    parent.appendChild(propertyBoxBg);
    parent.appendChild(propertyBox);
    
    var rt = new rootabs(this.id + "Tabs", {
        height: (propertyBox.offsetWidth - 10) + 'px',
        width: (propertyBox.offsetHeight - 40) + 'px'
    })
    rt.appendTo(propertyBox);
    rt.onAdd = function(newTab){
        rt.select(newTab.id);
    }
    
    // Make the divs resizable.
    $('#' + this.id).resizable({
        containment: '#' + parent.id,
		start: function(event, ui){
			thisObject.onBeforeResize(document.getElementById(thisObject.id).offsetWidth, document.getElementById(thisObject.id).offsetHeight, thisObject)
		},
        resize: function(event, ui){
            thisObject.setWidth(document.getElementById(thisObject.id).offsetWidth);
            thisObject.setHeight(document.getElementById(thisObject.id).offsetHeight);
			thisObject.onResize(document.getElementById(thisObject.id).offsetWidth, document.getElementById(thisObject.id).offsetHeight, thisObject);
        },
        stop: function(event, ui){
            thisObject.setWidth(document.getElementById(thisObject.id).offsetWidth);
            thisObject.setHeight(document.getElementById(thisObject.id).offsetHeight);
			thisObject.onAfterResize(document.getElementById(thisObject.id).offsetWidth, document.getElementById(thisObject.id).offsetHeight, thisObject);
        },
        minWidth: 100,
        minHeight: 50,
        maxWidth: 1024,
        maxHeight: 768
    });
    
    // Make the divs draggable.
    $('#' + this.id).draggable({
        handle: '#' + this.id + 'Header',
        containment: '#' + parent.id,
        drag: function(event, ui){
            thisObject.setTop(document.getElementById(thisObject.id).offsetTop);
            thisObject.setLeft(document.getElementById(thisObject.id).offsetLeft);
			thisObject.onDrag(document.getElementById(thisObject.id).offsetTop, document.getElementById(thisObject.id).offsetLeft, thisObject);
        },
        stop: function(event, ui){
            thisObject.setTop(document.getElementById(thisObject.id).offsetTop);
            thisObject.setLeft(document.getElementById(thisObject.id).offsetLeft);
			thisObject.onDrop(document.getElementById(thisObject.id).offsetTop, document.getElementById(thisObject.id).offsetLeft, thisObject);
        }
    });
    
	/**
	 * Add tabs to the property box.
	 * @param {String} title The title of the tab.
	 * @param {Int} position The position where the tab is going to be inserted.
	 * @return The id of the new tab. 
	 * @type {String}
	 */
	this.addTab = function(title, position){
        // Adds a tab to the end of the tab panel.
        // TabArray is the array with the title of the tabs.	
        if (position == null) {
            position = 0;
        }
        
        if (title == null) {
            title = 'myTab';
        }
        
        tabId = rt.add(title, position);
        
		this.onAddTab(title, position, propertyBox);
		
        return tabId;
    }
    
	/**
	 * Removes a tab.
	 * @param {Int} position The position of the tab to be removed.
	 */
    this.removeTab = function(position){
        //removes a tab from the tab panel.
        //position is the position of the tab you want to remove	
		this.onRemoveTab(position, this);
			
        if (position == null) {
            document.write('Error: propertyBox -> removeTab : position is null.');
            return;
        }
        
        var tabLength = rt.tabs.length;
        
        //verifies if the number of the tab really exists
        if ((position > 0) && (position < tabLength)) {
            //removes the tab
            rt.remove(position, 1);
        }
        else {
            document.write('Error: propertyBox -> removeTab : ' + position + ' is not a valid position.');
            return;
        }
    }

	/**
	 * Defines what is going to be done when you add a property.
	 * @param {Int} tabId The id of the tab. Received when it was added. 
	 * @param {Int} row The row in the table inside the tab where the content is going to be inserted.
	 * @param {Int} cell The cell in the table inside the tab where the content is going to be inserted.
	 * @param {String} property The property of the EO to be changed by this field.
	 * @param {String} inputType The type of input to be inserted. Eg.: Text, Color, FontColor
	 * @param {Object} EO The EO that is going to have its property changed.
	 * @param {Object} properties The properties you may use depending on the type of field of your choice. 
	 */		
	this.addProperty = function(tabId, row, cell, property, inputType, EO, properties){
        if (tabId == null) {
            return;
        }
		
		row = row ? row:0;
		cell = cell ? cell:0;
		
        // Gets the table we want to change
        tabId = String(tabId);
        tabIdNumber = parseInt(tabId.substring(rt.tabPrefix.length));
        var tabTableId = 'tabTable-' + tabIdNumber;
        var tabTable = new table(tabTableId);
        tabTable.table.className = 'property-box-tab-table';
        tabTable.table.border = '0';
		tabTable.table.align = 'left';
        var tabDiv = rt.getTabById(tabId).panel;
        if (tabDiv != null) {
            if (document.getElementById(tabTable.table.id) == null) {
                tabDiv.appendChild(tabTable.table);
            }
        }	
		
		properties.label = properties.label ? properties.label : "";
		properties.help = properties.help ? properties.help : "";
		properties.property = property;
		properties.EO = EO?EO: null;
		
		var arr = new window['input' + inputType](properties);				 
		
		for (var i =0; i < arr.length; i++){
			tabTable.insertElement(
				row + arr[i].row, 
				cell + arr[i].cell, 
				arr[i].rowspan, 
				arr[i].colspan, 
				arr[i].content
			);	
		}
		
		this.onAddProperty(tabId, row, cell, property, inputType, EO, properties, this);
		
	}
    
	/**
	 * Clears the content of a tab.
	 * @param {String} tabId The id of the tab to be cleared.
	 */
    this.clearTabContent = function(tabId){
        //clears the content of the div that contains the tab content.
        //tabId is the id of the tab you want to clear. The number in the id, not the position.
        if (tabId == null) {
            document.write('Error: propertyBox -> clearTabContent : tabId is null.');
            return;
        }
        else {
            var tabLength = rt.tabs.length;
            
            //verifies if the number of the tab really exists
            if ((tabId > 0) || (tabId < tabLength)) {
                //destroy the table inside the div
                var tabDivId = 'tab-' + tabId;
                var tabTableId = 'tabTable-' + tabId;
                document.getElementById(tabDivId).removeChild(document.getElementById(tabTableId));
                
                //the table inside the tab, where the DOM objects to change the objects properties are placed.
                var tabTable = document.createElement('table');
                tabTable.id = 'tabTable-' + tabId;
                tabTable.className = 'tabTable';
                tabTable.border = 0;
                
                //appends the new table to the div								
                document.getElementById(tabDivId).appendChild(tabTable);
            }
            else {
                document.write('Error: propertyBox -> clearTabContent : ' + tabId + ' is not a valid id for a tab.');
                return;
            }
        }
		
		this.onRemoveAllProperties(tabId, this);		
    }
    
	/**
	 * Removes all the tabs of the property box.
	 */
    this.removeAllTabs = function(){
        // Removes all the tabs 
        var lenTabs = rt.tabs.length;
        for (var i = 0; i < lenTabs; i++) {
            rt.remove(0, 1);
        }
    }
	
	/**
	 * Sets the left property of the property box. 
	 * @param {Int} left The left coordinate to be set.
	 */
    this.setLeft = function(left){
        propertyBoxBg.style.left = left + 'px';
        if (navigator.appName != "Microsoft Internet Explorer") {
            propertyBox.style.left = left + parseInt(propertyBoxBg.style.borderLeftWidth) + 'px';
        }
        else {
            propertyBox.style.left = left + 'px';
        }
        this.left = left;
    }

	/**
	 * Sets the top property of the property box
	 * @param {Int} top The top coordinate to be set.
	 */
    this.setTop = function(top){
        propertyBoxBg.style.top = top + 'px';
        if (navigator.appName != "Microsoft Internet Explorer") {
            propertyBox.style.top = top + parseInt(propertyBoxBg.style.borderTopWidth) + 'px';
        }
        else {
            propertyBox.style.top = top + 'px';
        }
        this.top = top;
    }
    
	/**
	 * Sets the width of the property box.
	 * @param {Int} width The width to be set.
	 */
    this.setWidth = function(width){
        propertyBoxBg.style.width = width + 'px';
        propertyBox.style.width = width + 'px';
        rt.setWidth(width - 10);
        this.width = width;
    }
    
	/**
	 * Sets the height of the property box.
	 * @param {Int} height The height to be set.
	 */
    this.setHeight = function(height){
        propertyBoxBg.style.height = height + 'px';
        propertyBox.style.height = height + 'px';
        rt.setHeight(height - 20);
        this.height = height;
    }
	
	/**
	 * Sets the title of the property box. 
	 * @param {String} title The new title to be set.
	 */
	this.setTitle = function(title){
		propertyBoxHeader.innerHTML = title;
		this.title = title;
	}
	
	/**
	 * Changes a property of the property box to some value
	 * @param {String} propertyName The name of the property to be changed.
	 * @param {Any} value The value to be set to the property.
	 */	
	this.setProperty = function(propertyName, value){
		switch(propertyName){
			case 'height':
				this.setHeight(value);
				break;
			
			case 'width':
				this.setWidth(value);
				break;
				
			case 'top':
				this.setTop(value);
				break;
			
			case 'left':
				this.setLeft(value);
				break;
			
			case 'title':
				this.setTitle(value);
				break;
				
			case 'cssPath':
				this.setCSS(value);
				break;
			
			default:
				document.write('Property not found');
				break;
		}
	}
	
    this.setLeft(this.left);
    this.setTop(this.top);
    this.setWidth(this.width);
    this.setHeight(this.height);
	
	/**
	 * Removes this property box.
	 */	
	this.remove = function(){
		propertyBox.parentNode.removeChild(propertyBox);
		propertyBoxBg.parentNode.removeChild(propertyBoxBg);
		propertyBoxHeader.parentNode.removeChild(propertyBoxHeader);
		delete propertyBox;
		delete propertyBoxBg;
		delete propertyBoxHeader;
		this.onRemove();
	}
	
	/**
	 * Sets a new CSS stylesheet to the property box.
	 * @param {String} path The relative path of the CSS stylesheet file from the APP_HOME folder.
	 */	
	this.setCSS = function(path){
		this.workspace.setCSS(this, path);
	}
	this.setCSS(this.cssPath);
}
